package org.jeecg.modules.solitaire_app.wxpay;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import lombok.extern.slf4j.Slf4j;
import net.sf.json.JSONObject;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.util.EntityUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.modules.solitaire_app.app.entity.YsyEmployeePrice;
import org.jeecg.modules.solitaire_app.app.entity.YsyMeal;
import org.jeecg.modules.solitaire_app.app.entity.YsyMealOrder;
import org.jeecg.modules.solitaire_app.app.entity.YsySystem;
import org.jeecg.modules.solitaire_app.app.mapper.WXLoginMapper;
import org.jeecg.modules.solitaire_app.app.mapper.YsyEmployeePriceMapper;
import org.jeecg.modules.solitaire_app.app.mapper.YsyMealOrderMapper;
import org.jeecg.modules.solitaire_app.app.service.IYsyEmployeePriceService;
import org.jeecg.modules.solitaire_app.app.service.IYsyMealService;
import org.jeecg.modules.solitaire_app.wxpay.v3.Util;
import org.jeecg.modules.solitaire_app.wxpay.v3.WXPayParams;
import org.jeecg.modules.solitaire_app.wxpay.v3.WXReturnPayParams;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
@Component
public class WxPayCommon {

    //微信支付
    private String wxPayUrl = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";

    //支付回调
    private String notifyUrl = "https://zcdapi.zxzkeji.com/ysyApi/app/wxpay/wxPayNotify";

    /*退款回调*/
    private String returnPayNotifyUrl = "https://zcdapi.zxzkeji.com/ysyApi/app/wxpay/returnPayNotify";

    //微信退款
    private String refunds = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds";
    @Autowired
    private WXLoginMapper wxLoginMapper;
    @Autowired
    private YsyMealOrderMapper ysyMealOrderMapper;
    @Autowired
    private IYsyEmployeePriceService ysyEmployeePriceService;
    @Autowired
    private IYsyMealService ysyMealService;

    /*微信支付*/
    public Result<?> wxPay(WXPayParams wxPayParams) throws IOException, InvalidKeySpecException, NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        Result<?> result = new Result<>();
        YsySystem saSystem = wxLoginMapper.getBSystem(wxPayParams.getAppId());
        if (ObjectUtil.isNull(saSystem)) {
            result.setCode(500);
            result.setSuccess(false);
            result.setMessage("小程序配置未找到");
            return result;
        }
        int totalPrice = 0;
        String description = null;
        if (wxPayParams.getSourceType() == 1 || wxPayParams.getSourceType() == 2) {
            YsyMealOrder ysyMealOrder = ysyMealOrderMapper.selectOne(Wrappers.<YsyMealOrder>lambdaQuery()
                    .eq(YsyMealOrder::getId, wxPayParams.getOrderId()));
            if (ObjectUtil.isNull(ysyMealOrder)) {
                result.setCode(500);
                result.setSuccess(false);
                result.setMessage("未找到该订单");
                return result;
            }

            YsyMeal ysyMeal = ysyMealService.getOne(Wrappers.<YsyMeal>lambdaQuery()
                    .eq(YsyMeal::getOriPrice,ysyMealOrder.getOriPrice())
                    .eq(YsyMeal::getType,ysyMealOrder.getSourceType()));

            if (ObjectUtil.isEmpty(ysyMeal)){
                result.setCode(500);
                result.setSuccess(false);
                result.setMessage("套餐不存在！！！");
                return result;
            }

            if (ysyMealOrder.getTotalPrice().compareTo(wxPayParams.getTotal()) != 0) {
                result.setCode(500);
                result.setSuccess(false);
                result.setMessage("订单金额不符");
                return result;
            }
            YsyEmployeePrice ysyEmployeePrice = new YsyEmployeePrice();
            //封车订单
            if (ysyMealOrder.getSourceType() == 1) {
                //查询封车内部员工价格
                ysyEmployeePrice = ysyEmployeePriceService.getOne(Wrappers.<YsyEmployeePrice>lambdaQuery()
                        .eq(YsyEmployeePrice::getOpenId, wxPayParams.getOpenid())
                        .eq(YsyEmployeePrice::getType, 1));
            } else {
                //查询轨迹内部员工价格
                ysyEmployeePrice = ysyEmployeePriceService.getOne(Wrappers.<YsyEmployeePrice>lambdaQuery()
                        .eq(YsyEmployeePrice::getOpenId, wxPayParams.getOpenid())
                        .eq(YsyEmployeePrice::getType, 2));
            }
            if (ObjectUtil.isNotEmpty(ysyEmployeePrice)) {
                //以设置的员工价进行支付
                totalPrice = ysyMeal.getEmployPrice().multiply(BigDecimal.valueOf(100)).intValue();
                ysyMealOrder.setTotalPrice(ysyMeal.getEmployPrice());
                //修改订单得实际付款金额
                ysyMealOrderMapper.updateById(ysyMealOrder);
            } else {
                //不在员工价以设置的套餐价格支付
                totalPrice = ysyMealOrder.getTotalPrice().multiply(BigDecimal.valueOf(100)).intValue();
            }

            description = ysyMealOrder.getOrderDesc();
        } else {
            result.setCode(500);
            result.setSuccess(false);
            result.setMessage("暂无该业务");
            return result;
        }


        String mchId = saSystem.getMchId();
        String privateKey = saSystem.getPrivateKey();
        String mchSerialNo = saSystem.getMchidSerialNo();
        String apiV3Key = saSystem.getKey();

        // 加载商户私钥（privateKey：私钥字符串）
        PrivateKey merchantPrivateKey = PemUtil
                .loadPrivateKey(new ByteArrayInputStream(privateKey.getBytes("utf-8")));
//        PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(
//                new FileInputStream("/mnt/cert/apiclient_key.pem"));

        // 加载平台证书（mchId：商户号,mchSerialNo：商户证书序列号,apiV3Key：V3秘钥）
        AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
                new WechatPay2Credentials(mchId, new PrivateKeySigner(mchSerialNo, merchantPrivateKey)), apiV3Key.getBytes("utf-8"));

        // 初始化httpClient
        // *********************** 此处有坑 HttpClient 与文档有出入
        HttpClient httpClient = WechatPayHttpClientBuilder.create()
                .withMerchant(saSystem.getMchId(), saSystem.getMchidSerialNo(), merchantPrivateKey)
                .withValidator(new WechatPay2Validator(verifier)).build();

        //随机字符串  根据自己工具类写
        String nonceStr = IdUtil.simpleUUID();
        //时间戳 根据自己工具类写
        String timeStamp = String.valueOf(System.currentTimeMillis());
        HttpPost httpPost = new HttpPost(this.wxPayUrl);
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectMapper objectMapper = new ObjectMapper();
        ObjectNode rootNode = objectMapper.createObjectNode();
        rootNode.put("mchid", saSystem.getMchId())
                .put("appid", saSystem.getAppid())
                .put("description", description)
                .put("attach", wxPayParams.getOrderId() + "&" + wxPayParams.getSourceType() + "&" + wxPayParams.getPayType())
                .put("notify_url", this.notifyUrl)
                .put("out_trade_no", timeStamp);
        rootNode.putObject("amount")
                .put("total", totalPrice);
        rootNode.putObject("payer")
                .put("openid", wxPayParams.getOpenid());
        objectMapper.writeValue(bos, rootNode);
        httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));

        //完成签名并执行请求
        String prepay_id = "";
        // *********************** 此处有坑 (CloseableHttpResponse)  与文档有出入，这样写可用
        CloseableHttpResponse response = (CloseableHttpResponse) httpClient.execute(httpPost);
        try {
            System.out.println(response.getStatusLine());
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == 200) {
                prepay_id = "prepay_id=" + JSONObject.fromObject(EntityUtils.toString(response.getEntity())).getString("prepay_id");
                ;
                //************************签名，自己拼接，然后进行两次加密，为什么加密，下面解释
                String pendingPaySign = saSystem.getAppid() + "\n" + timeStamp + "\n" + nonceStr + "\n" + prepay_id + "\n";
                String paySign = Util.encodeBase64(Util.sign256(pendingPaySign, merchantPrivateKey));
                Map<String, Object> resultMap = new HashMap<>();
                resultMap.put("appId", saSystem.getAppid());
                resultMap.put("timeStamp", timeStamp);
                resultMap.put("nonceStr", nonceStr);
                resultMap.put("package", prepay_id);
                resultMap.put("signType", "RSA");
                resultMap.put("paySign", paySign);
                result.setSuccess(true);
                result.setCode(200);
                result.setMessage(JSON.toJSONString(resultMap));
                return result;
            } else if (statusCode == 204) {
                prepay_id = "success";
                result.setMessage("签名失败");
                result.setCode(204);
                result.setSuccess(false);
                return result;
            } else {
                prepay_id = "failed,resp code = " + statusCode + ",return body = " + EntityUtils.toString(response.getEntity());
                log.error("request failed");
                result.setMessage("参数错误");
                result.setCode(400);
                result.setSuccess(false);
                return result;
            }
        } finally {
            response.close();
        }
    }

    /*微信退款*/
    public Result<?> returnPay(WXReturnPayParams wxReturnPayParams) throws IOException {
        Result<?> result = new Result<>();
        YsySystem saSystem = wxLoginMapper.getBSystem(wxReturnPayParams.getAppId());
        if (ObjectUtil.isNull(saSystem)) {
            result.setCode(500);
            result.setSuccess(false);
            result.setMessage("小程序配置未找到");
            return result;
        }
        // 加载商户私钥（privateKey：私钥字符串）
        PrivateKey merchantPrivateKey = PemUtil
                .loadPrivateKey(new ByteArrayInputStream(saSystem.getPrivateKey().getBytes("utf-8")));
//        PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(
//                new FileInputStream("/mnt/cert/apiclient_key.pem"));

        // 加载平台证书（mchId：商户号,mchSerialNo：商户证书序列号,apiV3Key：V3秘钥）
        AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
                new WechatPay2Credentials(saSystem.getMchId(), new PrivateKeySigner(saSystem.getMchidSerialNo(), merchantPrivateKey)), saSystem.getKey().getBytes("utf-8"));

        // 初始化httpClient
        // *********************** 此处有坑 HttpClient 与文档有出入
        HttpClient httpClient = WechatPayHttpClientBuilder.create()
                .withMerchant(saSystem.getMchId(), saSystem.getMchidSerialNo(), merchantPrivateKey)
                .withValidator(new WechatPay2Validator(verifier)).build();

        //随机字符串  根据自己工具类写
        String nonceStr = IdUtil.simpleUUID();
        //时间戳 根据自己工具类写
        String timeStamp = String.valueOf(System.currentTimeMillis());
        HttpPost httpPost = new HttpPost(this.refunds);
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectMapper objectMapper = new ObjectMapper();
        ObjectNode rootNode = objectMapper.createObjectNode();
        rootNode.put("transaction_id", wxReturnPayParams.getTransactionId())
                .put("notify_url", this.returnPayNotifyUrl)
                .put("out_refund_no", timeStamp);
        rootNode.putObject("amount")
                .put("refund", (int) (wxReturnPayParams.getRefund() * 100))
                .put("total", (int) (wxReturnPayParams.getTotal() * 100))
                .put("currency", "CNY");
        objectMapper.writeValue(bos, rootNode);
        httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
        CloseableHttpResponse response = (CloseableHttpResponse) httpClient.execute(httpPost);
        try {
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == 200) {
                result.setMessage("退款成功");
                result.setCode(200);
                result.setSuccess(true);
                return result;
            } else {
                result.setSuccess(false);
                result.setCode(500);
                result.setMessage("退款失败");
                return result;
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            result.setMessage("服务器响应失败");
            result.setCode(500);
            result.setSuccess(false);
            return result;
        }

    }
}
